An agent reports what it could not get past, and it stays in the run's log (#1500) - #1631
Merged
Conversation
…s log (#1500) An agent that hits something only the user can fix had one way to say so: prose in its reply, which the reader has to notice. `update_tickets.md` even spelled the instruction out per preset ("show an error to the user"), with no mechanism behind it. It now emits an `error` block, and the framework records it as an event on the run's own timeline — a headline and the detail below it, red in the log at the point it happened, counted on the session overview so it survives the log scrolling past. An error is an event, not a state: nothing clears it, because nothing can un-happen it. That is what makes it different from the project errors a background job finds between runs (project-errors.ts) — those are conditions that are true now and clear themselves when the condition is gone. Two natures, two homes, one red vocabulary. - every error block in a turn is kept: two things going wrong are two errors - an error is recorded once however often the agent restates its block, so an agent repeating itself does not read as the same failure happening over and over - update_tickets.md now says which conditions are errors and lets the protocol own how
) The parser and the fold were unit-tested, but nothing proved the turn emitter is actually wired into a run. The backlog-turn signals test now emits an `error` block alongside the view, session name and ready-for-merge it already covered, and asserts the event that comes out — verified by unwiring the emitter and watching it fail.
…is (#1500) Dogfooding found it: the count was only rendered by AgentOverview, which lives on the PROJECT page. Open a run by its URL — the page that actually carries the log — and the count was nowhere. The whole point of it is to survive the log scrolling. It is now its own component, used by both headers: the run's action bar and the project page's overview. Placing it also had to be decided rather than guessed. In the action bar it sits with the controls, not among the branch facts, which give up width as the row fills — the first attempt put it there and it rendered as "1 erro" against the summary with no gap. It never shrinks now, and the headline only renders where the row has room for it (the overview); in the tight bar every headline is the hover instead. Verified on two real local runs against a seeded project: - update_tickets on a tickets/ with no lastImportedAt: the agent reached for the block unprompted, one red row in the log, "1 error" in the bar - a two-failure task: two separate rows, "2 errors", both headlines in the hover
suleimansh
marked this pull request as ready for review
August 21, 2026 23:24
suleimansh
added a commit
that referenced
this pull request
Aug 22, 2026
…s log (#1500) (#1631) * An agent reports what it could not get past, and it stays in the run's log (#1500) An agent that hits something only the user can fix had one way to say so: prose in its reply, which the reader has to notice. `update_tickets.md` even spelled the instruction out per preset ("show an error to the user"), with no mechanism behind it. It now emits an `error` block, and the framework records it as an event on the run's own timeline — a headline and the detail below it, red in the log at the point it happened, counted on the session overview so it survives the log scrolling past. An error is an event, not a state: nothing clears it, because nothing can un-happen it. That is what makes it different from the project errors a background job finds between runs (project-errors.ts) — those are conditions that are true now and clear themselves when the condition is gone. Two natures, two homes, one red vocabulary. - every error block in a turn is kept: two things going wrong are two errors - an error is recorded once however often the agent restates its block, so an agent repeating itself does not read as the same failure happening over and over - update_tickets.md now says which conditions are errors and lets the protocol own how * Prove the error signal reaches the stream on an agent's real path (#1500) The parser and the fold were unit-tested, but nothing proved the turn emitter is actually wired into a run. The backlog-turn signals test now emits an `error` block alongside the view, session name and ready-for-merge it already covered, and asserts the event that comes out — verified by unwiring the emitter and watching it fail. * The error count belongs on the run page, where the log it summarises is (#1500) Dogfooding found it: the count was only rendered by AgentOverview, which lives on the PROJECT page. Open a run by its URL — the page that actually carries the log — and the count was nowhere. The whole point of it is to survive the log scrolling. It is now its own component, used by both headers: the run's action bar and the project page's overview. Placing it also had to be decided rather than guessed. In the action bar it sits with the controls, not among the branch facts, which give up width as the row fills — the first attempt put it there and it rendered as "1 erro" against the summary with no gap. It never shrinks now, and the headline only renders where the row has room for it (the overview); in the tight bar every headline is the hover instead. Verified on two real local runs against a seeded project: - update_tickets on a tickets/ with no lastImportedAt: the agent reached for the block unprompted, one red row in the log, "1 error" in the bar - a two-failure task: two separate rows, "2 errors", both headlines in the hover
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 curated
Closes #1500 (the capability half), built as Option A from the comment you approved.
What an agent can now do
The first line is the headline, the rest is the detail. Non-blocking — it does not stop the turn and it does not ask the user anything (
AWAITis still the way to ask).Where it shows up
isFailure), so it gets the same red text and red wash as a driver error or a failed run — no second vocabulary for the same thing.2 errors · <the latest headline>. The log scrolls; this does not. This is the "pin" and the "3 errors" you asked for, in the place where the overview cards already fold the event stream.Why nothing clears it
An error is an event: it happened at a point in the run and stays there as history. Nothing dismisses it because nothing can un-happen it — which is what removed the dismiss/expire question I was stuck on.
That leaves
project-errors.tsdoing the other half, unchanged: the errors a background job finds between runs are conditions that are true now, and they clear themselves when the condition is gone. Both SPECs now say which is which.Two rules worth knowing
The prompt side
update_tickets.mdhad the hard-wired line this replaces ("show an error to the user"). It now names which conditions are errors and lets the protocol own how — the same split the other signals use.Tests
Six new, each proven by breaking the code it guards and watching exactly that test go red: the dedupe, keeping every block, skipping empty blocks, the fold keeping the detail, and both halves of the red lane. Full suite green (1536 node + 779 dashboard).
Not manually verified in the running app — that needs a daemon restart, which is yours to make.